在DAY 22重新設定Entity Framework的時候,其中有個步驟是確認要使用的資料連結為Azure的之後,選擇「是,在連接字串中包含敏感性資料」,這樣的作法其實會將我們登入資料庫的帳密明白地顯示在連接字串中。
設定完Entity Framework後,我們打開專案的Web.config檔案來一探究竟,找到<connectionStrings>標籤就可以看到設定的連接字串內容,裡面也能清楚地看到資料庫的密碼是什麼。

能找到程式碼並用肉眼就能看出密碼為何,光想就覺得不安全吧?如果密碼能先經過加密,要使用連線字串時再將密碼解密傳入,至少被破解的機會比較小,下面我們就來說明該如何實現連線字串加密的方式。
首先在建立好的實體模型找到ShoppingCarAzure.Context.cs檔案並開啟。
開啟後可以看到裡面的dbShoppingCarAzureEntities類別繼承了DbContext,且有一個不帶參數的建構子,其中的base("name=dbShoppingCarAzureEntities")可以對應到Web.config內<connectionStrings>標籤下的<add name="dbShoppingCarAzureEntities".../>內容。
接著來新增一個該類別的建構子,且需帶有連接字串參數,不過因為ShoppingCarAzure.Context.cs是由範本產生,直接修改裡面內容可能會有未預期的問題,所以我們選擇新增一個同名的partial class來解決。在Model資料夾底下新增dbShoppingCarAzureEntities.cs,接著新增建構子如下:
    public partial class dbShoppingCarAzureEntities
    {
        public dbShoppingCarAzureEntities(string cnStr) : base(cnStr)
        {
        }
    }
注意class前面要加上partial關鍵字,另外建構子的快捷鍵是ctor,打完按2下Tab鍵就可以自動產生。
接著我們需要新增加解密的方法,在Model資料夾底下加入EncryptService類別,底下會有兩個方法EncryptBase64()與DecryptBase64()分別處理將字串加密轉成Base64格式以及將Base64格式解密回原本字串。這邊不深入說明加解密的詳細步驟,先知道如何用即可,下面Code可以直接取用複製:
public class EncryptService
    {
        //可自行替換key與iv內容
        static string keystr = "01234567";
        static string ivstr = "abcdefgh";
        public static string EncryptBase64(string SourceStr)
        {
            string encrypt = string.Empty;
            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider();
            byte[] key = sha256.ComputeHash(Encoding.UTF8.GetBytes(keystr));
            byte[] iv = md5.ComputeHash(Encoding.UTF8.GetBytes(ivstr));
            aes.Key = key;
            aes.IV = iv;
            byte[] dataByteArray = Encoding.UTF8.GetBytes(SourceStr);
            using (MemoryStream ms = new MemoryStream())
            using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(dataByteArray, 0, dataByteArray.Length);
                cs.FlushFinalBlock();
                encrypt = Convert.ToBase64String(ms.ToArray());
            }
            return encrypt;
        }
        public static string DecryptBase64(string EncryptStr)
        {
            string decrypt = string.Empty;
            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider();
            byte[] key = sha256.ComputeHash(Encoding.UTF8.GetBytes(keystr));
            byte[] iv = md5.ComputeHash(Encoding.UTF8.GetBytes(ivstr));
            aes.Key = key;
            aes.IV = iv;
            byte[] dataByteArray = Convert.FromBase64String(EncryptStr);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(dataByteArray, 0, dataByteArray.Length);
                    cs.FlushFinalBlock();
                    decrypt = Encoding.UTF8.GetString(ms.ToArray());
                }
            }
            return decrypt;
        }
    }
Base64格式: https://zh.wikipedia.org/zh-tw/Base64
加解密方法準備好後,接著來修改Web.config內容,步驟如下:
首先當然是要把連線字串的密碼部分password=xxxxxxxxxxxx;砍掉,刪除後將connectionString內的文字metadata=res://......MultipleActiveResultSets=True;App=EntityFramework"整段複製。
找到<appSettings>標籤,在裡面加入一行內容如下Code:
<add key="db_ConnStr" value="metadata=res:......MultipleActiveResultSets=True;App=EntityFramework;" />
這邊的value內容是刪除密碼後的連線字串,但注意最後分號;前面少了"(雙引號),因為後面還要再將密碼組合上去,到時候在字串尾端再補上雙引號。
再來就是要將密碼加密取得加密後字串了,我自己是偷在動作方法內新增變數,下中斷點複製變數的值,取得後刪除變數。(圖中的"mypassword"請替換成自己資料庫連線的密碼)
或者要另外新建一個ConsoleApp之類的來取得也行,總之我只是要先拿到加密後字串。
接著比照步驟2新增加密後密碼字串,Code如下。(value內容不要抄下面的啊!是要放自己加密後的密碼。)
<add key="db_Pwd" value="61Lxfh4uIrKZfFB4TBcdRQ==" /><!--加密後的密碼-->

再來我們建立一個方法可以來組合連線字串+密碼,並回傳帶有連線字串的db物件實體。首先建立ConnectStringService類別,接著新增下列Code:
    public class ConnectStringService
    {
        public static dbShoppingCarAzureEntities CreateDBContext()
        {
            var db_ConnStr = ConfigurationManager.AppSettings["db_ConnStr"];
            var db_Pwd = EncryptService.DecryptBase64(ConfigurationManager.AppSettings["db_Pwd"]);
            var db_ConnStrFull = $@"{db_ConnStr}password={db_Pwd}""";
            return new dbShoppingCarAzureEntities(db_ConnStrFull);
        }
    }
這邊的重點是,要取得Web.config的<appSettings>內容的話,我們利用ConfigurationManager來存取,使用時要using System.Configuration;。另外在組合完整連線字串的時候,最後面要補上雙引號(記得我們在前面提到的),雙引號的跳脫方式是多輸入1個雙引號。
最後一步就是將HomeController和MemberController內宣告的db物件改用上述新建的方法取得,Code如下:
        //取代 dbShoppingCarAzureEntities db = new dbShoppingCarAzureEntities();
        dbShoppingCarAzureEntities db = ConnectStringService.CreateDBContext();
修改完成後執行網站看是否能夠正確連線到資料庫,能顯示首頁內容就代表連接沒問題。
上述範例僅有將密碼從連線字串中拆開並加密,如果想要將帳號或其他連線字串內容也分別加密的話也可以,比照上面步驟各自處理加密再組合即可。
這次的內容主要參考下列文章,自己消化後再改寫成個人的版本。
https://dotblogs.com.tw/h20/2017/12/25/142557
那麼今天就到這囉~See U